<?xml version="1.0" encoding="utf-8" standalone="no"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
        "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title></title>
    <link href="../Styles/sure3.css" rel="stylesheet" type="text/css"/>
    <link href="../Styles/base1.css" rel="stylesheet" type="text/css"/>
</head>

<body>
<div class="markdown-body entry-content" itemprop="text">
    <h1><a href="#promise-对象"  class="anchor" id="user-content-promise-对象">

    </a>Promise 对象
    </h1>
    <h2><a href="#promise-的含义"  class="anchor" id="user-content-promise-的含义">

    </a>Promise 的含义
    </h2>
    <p>Promise 是异步编程的一种解决方案，比传统的解决方案——回调函数和事件——更合理和更强大。它由社区最早提出和实现，ES6 将其写进了语言标准，统一了用法，原生提供了<code>Promise</code>对象。</p>
    <p>所谓<code>Promise</code>，简单说就是一个容器，里面保存着某个未来才会结束的事件（通常是一个异步操作）的结果。从语法上说，Promise 是一个对象，从它可以获取异步操作的消息。Promise 提供统一的
        API，各种异步操作都可以用同样的方法进行处理。</p>
    <p><code>Promise</code>对象有以下两个特点。</p>
    <p>（1）对象的状态不受外界影响。<code>Promise</code>对象代表一个异步操作，有三种状态：<code>pending</code>（进行中）、<code>fulfilled</code>（已成功）和<code>rejected</code>（已失败）。只有异步操作的结果，可以决定当前是哪一种状态，任何其他操作都无法改变这个状态。这也是<code>Promise</code>这个名字的由来，它的英语意思就是“承诺”，表示其他手段无法改变。
    </p>
    <p>
        （2）一旦状态改变，就不会再变，任何时候都可以得到这个结果。<code>Promise</code>对象的状态改变，只有两种可能：从<code>pending</code>变为<code>fulfilled</code>和从<code>pending</code>变为<code>rejected</code>。只要这两种情况发生，状态就凝固了，不会再变了，会一直保持这个结果，这时就称为
        resolved（已定型）。如果改变已经发生了，你再对<code>Promise</code>对象添加回调函数，也会立即得到这个结果。这与事件（Event）完全不同，事件的特点是，如果你错过了它，再去监听，是得不到结果的。
    </p>
    <p>注意，为了行文方便，本章后面的<code>resolved</code>统一只指<code>fulfilled</code>状态，不包含<code>rejected</code>状态。</p>
    <p>有了<code>Promise</code>对象，就可以将异步操作以同步操作的流程表达出来，避免了层层嵌套的回调函数。此外，<code>Promise</code>对象提供统一的接口，使得控制异步操作更加容易。</p>
    <p><code>Promise</code>也有一些缺点。首先，无法取消<code>Promise</code>，一旦新建它就会立即执行，无法中途取消。其次，如果不设置回调函数，<code>Promise</code>内部抛出的错误，不会反应到外部。第三，当处于<code>pending</code>状态时，无法得知目前进展到哪一个阶段（刚刚开始还是即将完成）。
    </p>
    <p>如果某些事件不断地反复发生，一般来说，使用 <a href="https://nodejs.org/api/stream.html" rel="nofollow">Stream</a>
        模式是比部署<code>Promise</code>更好的选择。</p>
    <h2><a href="#基本用法"  class="anchor" id="user-content-基本用法">

    </a>基本用法
    </h2>
    <p>ES6 规定，<code>Promise</code>对象是一个构造函数，用来生成<code>Promise</code>实例。</p>
    <p>下面代码创造了一个<code>Promise</code>实例。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">const</span> <span
            class="pl-c1">promise</span> <span class="pl-k">=</span> <span class="pl-k">new</span> <span class="pl-en">Promise</span>(<span
            class="pl-k">function</span>(<span class="pl-smi">resolve</span>, <span class="pl-smi">reject</span>) {
  <span class="pl-c"><span class="pl-c">//</span> ... some code</span>

  <span class="pl-k">if</span> (<span class="pl-c"><span class="pl-c">/*</span> 异步操作成功 <span
                class="pl-c">*/</span></span>){
    <span class="pl-en">resolve</span>(value);
  } <span class="pl-k">else</span> {
    <span class="pl-en">reject</span>(error);
  }
});</pre>
    </div>
    <p><code>Promise</code>构造函数接受一个函数作为参数，该函数的两个参数分别是<code>resolve</code>和<code>reject</code>。它们是两个函数，由 JavaScript
        引擎提供，不用自己部署。</p>
    <p><code>resolve</code>函数的作用是，将<code>Promise</code>对象的状态从“未完成”变为“成功”（即从 pending 变为
        resolved），在异步操作成功时调用，并将异步操作的结果，作为参数传递出去；<code>reject</code>函数的作用是，将<code>Promise</code>对象的状态从“未完成”变为“失败”（即从
        pending 变为 rejected），在异步操作失败时调用，并将异步操作报出的错误，作为参数传递出去。</p>
    <p><code>Promise</code>实例生成以后，可以用<code>then</code>方法分别指定<code>resolved</code>状态和<code>rejected</code>状态的回调函数。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-smi">promise</span>.<span class="pl-en">then</span>(<span
            class="pl-k">function</span>(<span class="pl-smi">value</span>) {
  <span class="pl-c"><span class="pl-c">//</span> success</span>
}, <span class="pl-k">function</span>(<span class="pl-smi">error</span>) {
  <span class="pl-c"><span class="pl-c">//</span> failure</span>
});</pre>
    </div>
    <p><code>then</code>方法可以接受两个回调函数作为参数。第一个回调函数是<code>Promise</code>对象的状态变为<code>resolved</code>时调用，第二个回调函数是<code>Promise</code>对象的状态变为<code>rejected</code>时调用。其中，第二个函数是可选的，不一定要提供。这两个函数都接受<code>Promise</code>对象传出的值作为参数。
    </p>
    <p>下面是一个<code>Promise</code>对象的简单例子。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">function</span> <span
            class="pl-en">timeout</span>(<span class="pl-smi">ms</span>) {
  <span class="pl-k">return</span> <span class="pl-k">new</span> <span class="pl-en">Promise</span>((<span
                class="pl-smi">resolve</span>, <span class="pl-smi">reject</span>) <span class="pl-k">=&gt;</span> {
    <span class="pl-c1">setTimeout</span>(resolve, ms, <span class="pl-s"><span class="pl-pds">'</span>done<span
                class="pl-pds">'</span></span>);
  });
}

<span class="pl-en">timeout</span>(<span class="pl-c1">100</span>).<span class="pl-en">then</span>((<span
                class="pl-smi">value</span>) <span class="pl-k">=&gt;</span> {
  <span class="pl-en">console</span>.<span class="pl-c1">log</span>(value);
});</pre>
    </div>
    <p>
        上面代码中，<code>timeout</code>方法返回一个<code>Promise</code>实例，表示一段时间以后才会发生的结果。过了指定的时间（<code>ms</code>参数）以后，<code>Promise</code>实例的状态变为<code>resolved</code>，就会触发<code>then</code>方法绑定的回调函数。
    </p>
    <p>Promise 新建后就会立即执行。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">let</span> promise <span
            class="pl-k">=</span> <span class="pl-k">new</span> <span class="pl-en">Promise</span>(<span class="pl-k">function</span>(<span
            class="pl-smi">resolve</span>, <span class="pl-smi">reject</span>) {
  <span class="pl-en">console</span>.<span class="pl-c1">log</span>(<span class="pl-s"><span class="pl-pds">'</span>Promise<span
                class="pl-pds">'</span></span>);
  <span class="pl-en">resolve</span>();
});

<span class="pl-smi">promise</span>.<span class="pl-en">then</span>(<span class="pl-k">function</span>() {
  <span class="pl-en">console</span>.<span class="pl-c1">log</span>(<span class="pl-s"><span class="pl-pds">'</span>resolved.<span
                class="pl-pds">'</span></span>);
});

<span class="pl-en">console</span>.<span class="pl-c1">log</span>(<span class="pl-s"><span
                class="pl-pds">'</span>Hi!<span class="pl-pds">'</span></span>);

<span class="pl-c"><span class="pl-c">//</span> Promise</span>
<span class="pl-c"><span class="pl-c">//</span> Hi!</span>
<span class="pl-c"><span class="pl-c">//</span> resolved</span></pre>
    </div>
    <p>上面代码中，Promise 新建后立即执行，所以首先输出的是<code>Promise</code>。然后，<code>then</code>方法指定的回调函数，将在当前脚本所有同步任务执行完才会执行，所以<code>resolved</code>最后输出。
    </p>
    <p>下面是异步加载图片的例子。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">function</span> <span class="pl-en">loadImageAsync</span>(<span
            class="pl-smi">url</span>) {
  <span class="pl-k">return</span> <span class="pl-k">new</span> <span class="pl-en">Promise</span>(<span class="pl-k">function</span>(<span
                class="pl-smi">resolve</span>, <span class="pl-smi">reject</span>) {
    <span class="pl-k">const</span> <span class="pl-c1">image</span> <span class="pl-k">=</span> <span
                class="pl-k">new</span> <span class="pl-en">Image</span>();

    <span class="pl-smi">image</span>.<span class="pl-en">onload</span> <span class="pl-k">=</span> <span class="pl-k">function</span>() {
      <span class="pl-en">resolve</span>(image);
    };

    <span class="pl-smi">image</span>.<span class="pl-en">onerror</span> <span class="pl-k">=</span> <span class="pl-k">function</span>() {
      <span class="pl-en">reject</span>(<span class="pl-k">new</span> <span class="pl-en">Error</span>(<span
                class="pl-s"><span class="pl-pds">'</span>Could not load image at <span
                class="pl-pds">'</span></span> <span class="pl-k">+</span> url));
    };

    <span class="pl-smi">image</span>.<span class="pl-smi">src</span> <span class="pl-k">=</span> url;
  });
}</pre>
    </div>
    <p>上面代码中，使用<code>Promise</code>包装了一个图片加载的异步操作。如果加载成功，就调用<code>resolve</code>方法，否则就调用<code>reject</code>方法。</p>
    <p>下面是一个用<code>Promise</code>对象实现的 Ajax 操作的例子。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">const</span> <span
            class="pl-c1">getJSON</span> <span class="pl-k">=</span> <span class="pl-k">function</span>(<span
            class="pl-smi">url</span>) {
  <span class="pl-k">const</span> <span class="pl-c1">promise</span> <span class="pl-k">=</span> <span
                class="pl-k">new</span> <span class="pl-en">Promise</span>(<span class="pl-k">function</span>(<span
                class="pl-smi">resolve</span>, <span class="pl-smi">reject</span>){
    <span class="pl-k">const</span> <span class="pl-c1">handler</span> <span class="pl-k">=</span> <span class="pl-k">function</span>() {
      <span class="pl-k">if</span> (<span class="pl-c1">this</span>.<span class="pl-c1">readyState</span> <span
                class="pl-k">!==</span> <span class="pl-c1">4</span>) {
        <span class="pl-k">return</span>;
      }
      <span class="pl-k">if</span> (<span class="pl-c1">this</span>.<span class="pl-c1">status</span> <span
                class="pl-k">===</span> <span class="pl-c1">200</span>) {
        <span class="pl-en">resolve</span>(<span class="pl-c1">this</span>.<span class="pl-smi">response</span>);
      } <span class="pl-k">else</span> {
        <span class="pl-en">reject</span>(<span class="pl-k">new</span> <span class="pl-en">Error</span>(<span
                class="pl-c1">this</span>.<span class="pl-c1">statusText</span>));
      }
    };
    <span class="pl-k">const</span> <span class="pl-c1">client</span> <span class="pl-k">=</span> <span
                class="pl-k">new</span> <span class="pl-en">XMLHttpRequest</span>();
    <span class="pl-smi">client</span>.<span class="pl-c1">open</span>(<span class="pl-s"><span class="pl-pds">"</span>GET<span
                class="pl-pds">"</span></span>, url);
    <span class="pl-smi">client</span>.<span class="pl-c1">onreadystatechange</span> <span class="pl-k">=</span> handler;
    <span class="pl-smi">client</span>.<span class="pl-smi">responseType</span> <span class="pl-k">=</span> <span
                class="pl-s"><span class="pl-pds">"</span>json<span class="pl-pds">"</span></span>;
    <span class="pl-smi">client</span>.<span class="pl-c1">setRequestHeader</span>(<span class="pl-s"><span
                class="pl-pds">"</span>Accept<span class="pl-pds">"</span></span>, <span class="pl-s"><span
                class="pl-pds">"</span>application/json<span class="pl-pds">"</span></span>);
    <span class="pl-smi">client</span>.<span class="pl-c1">send</span>();

  });

  <span class="pl-k">return</span> promise;
};

<span class="pl-en">getJSON</span>(<span class="pl-s"><span class="pl-pds">"</span>/posts.json<span
                class="pl-pds">"</span></span>).<span class="pl-en">then</span>(<span class="pl-k">function</span>(<span
                class="pl-smi">json</span>) {
  <span class="pl-en">console</span>.<span class="pl-c1">log</span>(<span class="pl-s"><span class="pl-pds">'</span>Contents: <span
                class="pl-pds">'</span></span> <span class="pl-k">+</span> json);
}, <span class="pl-k">function</span>(<span class="pl-smi">error</span>) {
  <span class="pl-en">console</span>.<span class="pl-c1">error</span>(<span class="pl-s"><span class="pl-pds">'</span>出错了<span
                class="pl-pds">'</span></span>, error);
});</pre>
    </div>
    <p>上面代码中，<code>getJSON</code>是对 XMLHttpRequest 对象的封装，用于发出一个针对 JSON 数据的 HTTP 请求，并且返回一个<code>Promise</code>对象。需要注意的是，在<code>getJSON</code>内部，<code>resolve</code>函数和<code>reject</code>函数调用时，都带有参数。
    </p>
    <p>
        如果调用<code>resolve</code>函数和<code>reject</code>函数时带有参数，那么它们的参数会被传递给回调函数。<code>reject</code>函数的参数通常是<code>Error</code>对象的实例，表示抛出的错误；<code>resolve</code>函数的参数除了正常的值以外，还可能是另一个
        Promise 实例，比如像下面这样。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">const</span> <span class="pl-c1">p1</span> <span
            class="pl-k">=</span> <span class="pl-k">new</span> <span class="pl-en">Promise</span>(<span class="pl-k">function</span> (<span
            class="pl-smi">resolve</span>, <span class="pl-smi">reject</span>) {
  <span class="pl-c"><span class="pl-c">//</span> ...</span>
});

<span class="pl-k">const</span> <span class="pl-c1">p2</span> <span class="pl-k">=</span> <span class="pl-k">new</span> <span
                class="pl-en">Promise</span>(<span class="pl-k">function</span> (<span
                class="pl-smi">resolve</span>, <span class="pl-smi">reject</span>) {
  <span class="pl-c"><span class="pl-c">//</span> ...</span>
  <span class="pl-en">resolve</span>(p1);
})</pre>
    </div>
    <p>上面代码中，<code>p1</code>和<code>p2</code>都是 Promise 的实例，但是<code>p2</code>的<code>resolve</code>方法将<code>p1</code>作为参数，即一个异步操作的结果是返回另一个异步操作。
    </p>
    <p>
        注意，这时<code>p1</code>的状态就会传递给<code>p2</code>，也就是说，<code>p1</code>的状态决定了<code>p2</code>的状态。如果<code>p1</code>的状态是<code>pending</code>，那么<code>p2</code>的回调函数就会等待<code>p1</code>的状态改变；如果<code>p1</code>的状态已经是<code>resolved</code>或者<code>rejected</code>，那么<code>p2</code>的回调函数将会立刻执行。
    </p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">const</span> <span class="pl-c1">p1</span> <span
            class="pl-k">=</span> <span class="pl-k">new</span> <span class="pl-en">Promise</span>(<span class="pl-k">function</span> (<span
            class="pl-smi">resolve</span>, <span class="pl-smi">reject</span>) {
  <span class="pl-c1">setTimeout</span>(() <span class="pl-k">=&gt;</span> <span class="pl-en">reject</span>(<span
                class="pl-k">new</span> <span class="pl-en">Error</span>(<span class="pl-s"><span
                class="pl-pds">'</span>fail<span class="pl-pds">'</span></span>)), <span class="pl-c1">3000</span>)
})

<span class="pl-k">const</span> <span class="pl-c1">p2</span> <span class="pl-k">=</span> <span class="pl-k">new</span> <span
                class="pl-en">Promise</span>(<span class="pl-k">function</span> (<span
                class="pl-smi">resolve</span>, <span class="pl-smi">reject</span>) {
  <span class="pl-c1">setTimeout</span>(() <span class="pl-k">=&gt;</span> <span class="pl-en">resolve</span>(p1), <span
                class="pl-c1">1000</span>)
})

p2
  .<span class="pl-en">then</span>(<span class="pl-smi">result</span> <span class="pl-k">=&gt;</span> <span
                class="pl-en">console</span>.<span class="pl-c1">log</span>(result))
  .<span class="pl-en">catch</span>(<span class="pl-smi">error</span> <span class="pl-k">=&gt;</span> <span
                class="pl-en">console</span>.<span class="pl-c1">log</span>(error))
<span class="pl-c"><span class="pl-c">//</span> Error: fail</span></pre>
    </div>
    <p>上面代码中，<code>p1</code>是一个 Promise，3 秒之后变为<code>rejected</code>。<code>p2</code>的状态在 1 秒之后改变，<code>resolve</code>方法返回的是<code>p1</code>。由于<code>p2</code>返回的是另一个
        Promise，导致<code>p2</code>自己的状态无效了，由<code>p1</code>的状态决定<code>p2</code>的状态。所以，后面的<code>then</code>语句都变成针对后者（<code>p1</code>）。又过了
        2 秒，<code>p1</code>变为<code>rejected</code>，导致触发<code>catch</code>方法指定的回调函数。</p>
    <p>注意，调用<code>resolve</code>或<code>reject</code>并不会终结 Promise 的参数函数的执行。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">new</span> <span
            class="pl-en">Promise</span>((<span class="pl-smi">resolve</span>, <span class="pl-smi">reject</span>) <span
            class="pl-k">=&gt;</span> {
  <span class="pl-en">resolve</span>(<span class="pl-c1">1</span>);
  <span class="pl-en">console</span>.<span class="pl-c1">log</span>(<span class="pl-c1">2</span>);
}).<span class="pl-en">then</span>(<span class="pl-smi">r</span> <span class="pl-k">=&gt;</span> {
  <span class="pl-en">console</span>.<span class="pl-c1">log</span>(r);
});
<span class="pl-c"><span class="pl-c">//</span> 2</span>
<span class="pl-c"><span class="pl-c">//</span> 1</span></pre>
    </div>
    <p>上面代码中，调用<code>resolve(1)</code>以后，后面的<code>console.log(2)</code>还是会执行，并且会首先打印出来。这是因为立即 resolved 的 Promise
        是在本轮事件循环的末尾执行，总是晚于本轮循环的同步任务。</p>
    <p>一般来说，调用<code>resolve</code>或<code>reject</code>以后，Promise 的使命就完成了，后继操作应该放到<code>then</code>方法里面，而不应该直接写在<code>resolve</code>或<code>reject</code>的后面。所以，最好在它们前面加上<code>return</code>语句，这样就不会有意外。
    </p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">new</span> <span
            class="pl-en">Promise</span>((<span class="pl-smi">resolve</span>, <span class="pl-smi">reject</span>) <span
            class="pl-k">=&gt;</span> {
  <span class="pl-k">return</span> <span class="pl-en">resolve</span>(<span class="pl-c1">1</span>);
  <span class="pl-c"><span class="pl-c">//</span> 后面的语句不会执行</span>
  <span class="pl-en">console</span>.<span class="pl-c1">log</span>(<span class="pl-c1">2</span>);
})</pre>
    </div>
    <h2><a href="#promiseprototypethen"  class="anchor" id="user-content-promiseprototypethen">

    </a>Promise.prototype.then()
    </h2>
    <p>Promise 实例具有<code>then</code>方法，也就是说，<code>then</code>方法是定义在原型对象<code>Promise.prototype</code>上的。它的作用是为 Promise
        实例添加状态改变时的回调函数。前面说过，<code>then</code>方法的第一个参数是<code>resolved</code>状态的回调函数，第二个参数（可选）是<code>rejected</code>状态的回调函数。
    </p>
    <p><code>then</code>方法返回的是一个新的<code>Promise</code>实例（注意，不是原来那个<code>Promise</code>实例）。因此可以采用链式写法，即<code>then</code>方法后面再调用另一个<code>then</code>方法。
    </p>
    <div class="highlight highlight-source-js"><pre><span class="pl-en">getJSON</span>(<span class="pl-s"><span
            class="pl-pds">"</span>/posts.json<span class="pl-pds">"</span></span>).<span
            class="pl-en">then</span>(<span class="pl-k">function</span>(<span class="pl-smi">json</span>) {
  <span class="pl-k">return</span> <span class="pl-smi">json</span>.<span class="pl-smi">post</span>;
}).<span class="pl-en">then</span>(<span class="pl-k">function</span>(<span class="pl-smi">post</span>) {
  <span class="pl-c"><span class="pl-c">//</span> ...</span>
});</pre>
    </div>
    <p>上面的代码使用<code>then</code>方法，依次指定了两个回调函数。第一个回调函数完成以后，会将返回结果作为参数，传入第二个回调函数。</p>
    <p>
        采用链式的<code>then</code>，可以指定一组按照次序调用的回调函数。这时，前一个回调函数，有可能返回的还是一个<code>Promise</code>对象（即有异步操作），这时后一个回调函数，就会等待该<code>Promise</code>对象的状态发生变化，才会被调用。
    </p>
    <div class="highlight highlight-source-js"><pre><span class="pl-en">getJSON</span>(<span class="pl-s"><span
            class="pl-pds">"</span>/post/1.json<span class="pl-pds">"</span></span>).<span
            class="pl-en">then</span>(<span class="pl-k">function</span>(<span class="pl-smi">post</span>) {
  <span class="pl-k">return</span> <span class="pl-en">getJSON</span>(<span class="pl-smi">post</span>.<span
                class="pl-smi">commentURL</span>);
}).<span class="pl-en">then</span>(<span class="pl-k">function</span> <span class="pl-en">funcA</span>(<span
                class="pl-smi">comments</span>) {
  <span class="pl-en">console</span>.<span class="pl-c1">log</span>(<span class="pl-s"><span class="pl-pds">"</span>resolved: <span
                class="pl-pds">"</span></span>, comments);
}, <span class="pl-k">function</span> <span class="pl-en">funcB</span>(<span class="pl-smi">err</span>){
  <span class="pl-en">console</span>.<span class="pl-c1">log</span>(<span class="pl-s"><span class="pl-pds">"</span>rejected: <span
                class="pl-pds">"</span></span>, err);
});</pre>
    </div>
    <p>上面代码中，第一个<code>then</code>方法指定的回调函数，返回的是另一个<code>Promise</code>对象。这时，第二个<code>then</code>方法指定的回调函数，就会等待这个新的<code>Promise</code>对象状态发生变化。如果变为<code>resolved</code>，就调用<code>funcA</code>，如果状态变为<code>rejected</code>，就调用<code>funcB</code>。
    </p>
    <p>如果采用箭头函数，上面的代码可以写得更简洁。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-en">getJSON</span>(<span class="pl-s"><span
            class="pl-pds">"</span>/post/1.json<span class="pl-pds">"</span></span>).<span class="pl-en">then</span>(
  <span class="pl-smi">post</span> <span class="pl-k">=&gt;</span> <span class="pl-en">getJSON</span>(<span
                class="pl-smi">post</span>.<span class="pl-smi">commentURL</span>)
).<span class="pl-en">then</span>(
  <span class="pl-smi">comments</span> <span class="pl-k">=&gt;</span> <span class="pl-en">console</span>.<span
                class="pl-c1">log</span>(<span class="pl-s"><span class="pl-pds">"</span>resolved: <span class="pl-pds">"</span></span>, comments),
  <span class="pl-smi">err</span> <span class="pl-k">=&gt;</span> <span class="pl-en">console</span>.<span
                class="pl-c1">log</span>(<span class="pl-s"><span class="pl-pds">"</span>rejected: <span class="pl-pds">"</span></span>, err)
);</pre>
    </div>
    <h2><a href="#promiseprototypecatch"  class="anchor" id="user-content-promiseprototypecatch">

    </a>Promise.prototype.catch()
    </h2>
    <p><code>Promise.prototype.catch</code>方法是<code>.then(null, rejection)</code>的别名，用于指定发生错误时的回调函数。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-en">getJSON</span>(<span class="pl-s"><span
            class="pl-pds">'</span>/posts.json<span class="pl-pds">'</span></span>).<span
            class="pl-en">then</span>(<span class="pl-k">function</span>(<span class="pl-smi">posts</span>) {
  <span class="pl-c"><span class="pl-c">//</span> ...</span>
}).<span class="pl-en">catch</span>(<span class="pl-k">function</span>(<span class="pl-smi">error</span>) {
  <span class="pl-c"><span class="pl-c">//</span> 处理 getJSON 和 前一个回调函数运行时发生的错误</span>
  <span class="pl-en">console</span>.<span class="pl-c1">log</span>(<span class="pl-s"><span class="pl-pds">'</span>发生错误！<span
                class="pl-pds">'</span></span>, error);
});</pre>
    </div>
    <p>上面代码中，<code>getJSON</code>方法返回一个 Promise 对象，如果该对象状态变为<code>resolved</code>，则会调用<code>then</code>方法指定的回调函数；如果异步操作抛出错误，状态就会变为<code>rejected</code>，就会调用<code>catch</code>方法指定的回调函数，处理这个错误。另外，<code>then</code>方法指定的回调函数，如果运行中抛出错误，也会被<code>catch</code>方法捕获。
    </p>
    <div class="highlight highlight-source-js"><pre><span class="pl-smi">p</span>.<span class="pl-en">then</span>((<span
            class="pl-smi">val</span>) <span class="pl-k">=&gt;</span> <span class="pl-en">console</span>.<span
            class="pl-c1">log</span>(<span class="pl-s"><span class="pl-pds">'</span>fulfilled:<span
            class="pl-pds">'</span></span>, val))
  .<span class="pl-en">catch</span>((<span class="pl-smi">err</span>) <span class="pl-k">=&gt;</span> <span
                class="pl-en">console</span>.<span class="pl-c1">log</span>(<span class="pl-s"><span
                class="pl-pds">'</span>rejected<span class="pl-pds">'</span></span>, err));

<span class="pl-c"><span class="pl-c">//</span> 等同于</span>
<span class="pl-smi">p</span>.<span class="pl-en">then</span>((<span class="pl-smi">val</span>) <span
                class="pl-k">=&gt;</span> <span class="pl-en">console</span>.<span class="pl-c1">log</span>(<span
                class="pl-s"><span class="pl-pds">'</span>fulfilled:<span class="pl-pds">'</span></span>, val))
  .<span class="pl-en">then</span>(<span class="pl-c1">null</span>, (<span class="pl-smi">err</span>) <span
                class="pl-k">=&gt;</span> <span class="pl-en">console</span>.<span class="pl-c1">log</span>(<span
                class="pl-s"><span class="pl-pds">"</span>rejected:<span class="pl-pds">"</span></span>, err));</pre>
    </div>
    <p>下面是一个例子。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">const</span> <span
            class="pl-c1">promise</span> <span class="pl-k">=</span> <span class="pl-k">new</span> <span class="pl-en">Promise</span>(<span
            class="pl-k">function</span>(<span class="pl-smi">resolve</span>, <span class="pl-smi">reject</span>) {
  <span class="pl-k">throw</span> <span class="pl-k">new</span> <span class="pl-en">Error</span>(<span
                class="pl-s"><span class="pl-pds">'</span>test<span class="pl-pds">'</span></span>);
});
<span class="pl-smi">promise</span>.<span class="pl-en">catch</span>(<span class="pl-k">function</span>(<span
                class="pl-smi">error</span>) {
  <span class="pl-en">console</span>.<span class="pl-c1">log</span>(error);
});
<span class="pl-c"><span class="pl-c">//</span> Error: test</span></pre>
    </div>
    <p>上面代码中，<code>promise</code>抛出一个错误，就被<code>catch</code>方法指定的回调函数捕获。注意，上面的写法与下面两种写法是等价的。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-c"><span class="pl-c">//</span> 写法一</span>
<span class="pl-k">const</span> <span class="pl-c1">promise</span> <span class="pl-k">=</span> <span
                class="pl-k">new</span> <span class="pl-en">Promise</span>(<span class="pl-k">function</span>(<span
                class="pl-smi">resolve</span>, <span class="pl-smi">reject</span>) {
  <span class="pl-k">try</span> {
    <span class="pl-k">throw</span> <span class="pl-k">new</span> <span class="pl-en">Error</span>(<span
                class="pl-s"><span class="pl-pds">'</span>test<span class="pl-pds">'</span></span>);
  } <span class="pl-k">catch</span>(e) {
    <span class="pl-en">reject</span>(e);
  }
});
<span class="pl-smi">promise</span>.<span class="pl-en">catch</span>(<span class="pl-k">function</span>(<span
                class="pl-smi">error</span>) {
  <span class="pl-en">console</span>.<span class="pl-c1">log</span>(error);
});

<span class="pl-c"><span class="pl-c">//</span> 写法二</span>
<span class="pl-k">const</span> <span class="pl-c1">promise</span> <span class="pl-k">=</span> <span
                class="pl-k">new</span> <span class="pl-en">Promise</span>(<span class="pl-k">function</span>(<span
                class="pl-smi">resolve</span>, <span class="pl-smi">reject</span>) {
  <span class="pl-en">reject</span>(<span class="pl-k">new</span> <span class="pl-en">Error</span>(<span
                class="pl-s"><span class="pl-pds">'</span>test<span class="pl-pds">'</span></span>));
});
<span class="pl-smi">promise</span>.<span class="pl-en">catch</span>(<span class="pl-k">function</span>(<span
                class="pl-smi">error</span>) {
  <span class="pl-en">console</span>.<span class="pl-c1">log</span>(error);
});</pre>
    </div>
    <p>比较上面两种写法，可以发现<code>reject</code>方法的作用，等同于抛出错误。</p>
    <p>如果 Promise 状态已经变成<code>resolved</code>，再抛出错误是无效的。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">const</span> <span
            class="pl-c1">promise</span> <span class="pl-k">=</span> <span class="pl-k">new</span> <span class="pl-en">Promise</span>(<span
            class="pl-k">function</span>(<span class="pl-smi">resolve</span>, <span class="pl-smi">reject</span>) {
  <span class="pl-en">resolve</span>(<span class="pl-s"><span class="pl-pds">'</span>ok<span
                class="pl-pds">'</span></span>);
  <span class="pl-k">throw</span> <span class="pl-k">new</span> <span class="pl-en">Error</span>(<span
                class="pl-s"><span class="pl-pds">'</span>test<span class="pl-pds">'</span></span>);
});
promise
  .<span class="pl-en">then</span>(<span class="pl-k">function</span>(<span class="pl-smi">value</span>) { <span
                class="pl-en">console</span>.<span class="pl-c1">log</span>(value) })
  .<span class="pl-en">catch</span>(<span class="pl-k">function</span>(<span class="pl-smi">error</span>) { <span
                class="pl-en">console</span>.<span class="pl-c1">log</span>(error) });
<span class="pl-c"><span class="pl-c">//</span> ok</span></pre>
    </div>
    <p>上面代码中，Promise 在<code>resolve</code>语句后面，再抛出错误，不会被捕获，等于没有抛出。因为 Promise 的状态一旦改变，就永久保持该状态，不会再变了。</p>
    <p>Promise 对象的错误具有“冒泡”性质，会一直向后传递，直到被捕获为止。也就是说，错误总是会被下一个<code>catch</code>语句捕获。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-en">getJSON</span>(<span class="pl-s"><span
            class="pl-pds">'</span>/post/1.json<span class="pl-pds">'</span></span>).<span
            class="pl-en">then</span>(<span class="pl-k">function</span>(<span class="pl-smi">post</span>) {
  <span class="pl-k">return</span> <span class="pl-en">getJSON</span>(<span class="pl-smi">post</span>.<span
                class="pl-smi">commentURL</span>);
}).<span class="pl-en">then</span>(<span class="pl-k">function</span>(<span class="pl-smi">comments</span>) {
  <span class="pl-c"><span class="pl-c">//</span> some code</span>
}).<span class="pl-en">catch</span>(<span class="pl-k">function</span>(<span class="pl-smi">error</span>) {
  <span class="pl-c"><span class="pl-c">//</span> 处理前面三个Promise产生的错误</span>
});</pre>
    </div>
    <p>上面代码中，一共有三个 Promise 对象：一个由<code>getJSON</code>产生，两个由<code>then</code>产生。它们之中任何一个抛出的错误，都会被最后一个<code>catch</code>捕获。
    </p>
    <p>一般来说，不要在<code>then</code>方法里面定义 Reject 状态的回调函数（即<code>then</code>的第二个参数），总是使用<code>catch</code>方法。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-c"><span class="pl-c">//</span> bad</span>
promise
  .<span class="pl-en">then</span>(<span class="pl-k">function</span>(<span class="pl-smi">data</span>) {
    <span class="pl-c"><span class="pl-c">//</span> success</span>
  }, <span class="pl-k">function</span>(<span class="pl-smi">err</span>) {
    <span class="pl-c"><span class="pl-c">//</span> error</span>
  });

<span class="pl-c"><span class="pl-c">//</span> good</span>
promise
  .<span class="pl-en">then</span>(<span class="pl-k">function</span>(<span class="pl-smi">data</span>) { <span
                class="pl-c"><span class="pl-c">//</span>cb</span>
    <span class="pl-c"><span class="pl-c">//</span> success</span>
  })
  .<span class="pl-en">catch</span>(<span class="pl-k">function</span>(<span class="pl-smi">err</span>) {
    <span class="pl-c"><span class="pl-c">//</span> error</span>
  });</pre>
    </div>
    <p>上面代码中，第二种写法要好于第一种写法，理由是第二种写法可以捕获前面<code>then</code>方法执行中的错误，也更接近同步的写法（<code>try/catch</code>）。因此，建议总是使用<code>catch</code>方法，而不使用<code>then</code>方法的第二个参数。
    </p>
    <p>跟传统的<code>try/catch</code>代码块不同的是，如果没有使用<code>catch</code>方法指定错误处理的回调函数，Promise 对象抛出的错误不会传递到外层代码，即不会有任何反应。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">const</span> <span
            class="pl-c1">someAsyncThing</span> <span class="pl-k">=</span> <span class="pl-k">function</span>() {
  <span class="pl-k">return</span> <span class="pl-k">new</span> <span class="pl-en">Promise</span>(<span class="pl-k">function</span>(<span
                class="pl-smi">resolve</span>, <span class="pl-smi">reject</span>) {
    <span class="pl-c"><span class="pl-c">//</span> 下面一行会报错，因为x没有声明</span>
    <span class="pl-en">resolve</span>(x <span class="pl-k">+</span> <span class="pl-c1">2</span>);
  });
};

<span class="pl-en">someAsyncThing</span>().<span class="pl-en">then</span>(<span class="pl-k">function</span>() {
  <span class="pl-en">console</span>.<span class="pl-c1">log</span>(<span class="pl-s"><span class="pl-pds">'</span>everything is great<span
                class="pl-pds">'</span></span>);
});

<span class="pl-c1">setTimeout</span>(() <span class="pl-k">=&gt;</span> { <span class="pl-en">console</span>.<span
                class="pl-c1">log</span>(<span class="pl-c1">123</span>) }, <span class="pl-c1">2000</span>);
<span class="pl-c"><span class="pl-c">//</span> Uncaught (in promise) ReferenceError: x is not defined</span>
<span class="pl-c"><span class="pl-c">//</span> 123</span></pre>
    </div>
    <p>上面代码中，<code>someAsyncThing</code>函数产生的 Promise 对象，内部有语法错误。浏览器运行到这一行，会打印出错误提示<code>ReferenceError: x is not
        defined</code>，但是不会退出进程、终止脚本执行，2 秒之后还是会输出<code>123</code>。这就是说，Promise 内部的错误不会影响到 Promise 外部的代码，通俗的说法就是“Promise
        会吃掉错误”。</p>
    <p>这个脚本放在服务器执行，退出码就是<code>0</code>（即表示执行成功）。不过，Node 有一个<code>unhandledRejection</code>事件，专门监听未捕获的<code>reject</code>错误，上面的脚本会触发这个事件的监听函数，可以在监听函数里面抛出错误。
    </p>
    <div class="highlight highlight-source-js"><pre><span class="pl-c1">process</span>.<span
            class="pl-en">on</span>(<span class="pl-s"><span class="pl-pds">'</span>unhandledRejection<span
            class="pl-pds">'</span></span>, <span class="pl-k">function</span> (<span class="pl-smi">err</span>, <span
            class="pl-smi">p</span>) {
  <span class="pl-k">throw</span> err;
});</pre>
    </div>
    <p>上面代码中，<code>unhandledRejection</code>事件的监听函数有两个参数，第一个是错误对象，第二个是报错的 Promise 实例，它可以用来了解发生错误的环境信息。</p>
    <p>注意，Node 有计划在未来废除<code>unhandledRejection</code>事件。如果 Promise 内部有未捕获的错误，会直接终止进程，并且进程的退出码不为 0。</p>
    <p>再看下面的例子。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">const</span> <span
            class="pl-c1">promise</span> <span class="pl-k">=</span> <span class="pl-k">new</span> <span class="pl-en">Promise</span>(<span
            class="pl-k">function</span> (<span class="pl-smi">resolve</span>, <span class="pl-smi">reject</span>) {
  <span class="pl-en">resolve</span>(<span class="pl-s"><span class="pl-pds">'</span>ok<span
                class="pl-pds">'</span></span>);
  <span class="pl-c1">setTimeout</span>(<span class="pl-k">function</span> () { <span class="pl-k">throw</span> <span
                class="pl-k">new</span> <span class="pl-en">Error</span>(<span class="pl-s"><span
                class="pl-pds">'</span>test<span class="pl-pds">'</span></span>) }, <span class="pl-c1">0</span>)
});
<span class="pl-smi">promise</span>.<span class="pl-en">then</span>(<span class="pl-k">function</span> (<span
                class="pl-smi">value</span>) { <span class="pl-en">console</span>.<span class="pl-c1">log</span>(value) });
<span class="pl-c"><span class="pl-c">//</span> ok</span>
<span class="pl-c"><span class="pl-c">//</span> Uncaught Error: test</span></pre>
    </div>
    <p>上面代码中，Promise 指定在下一轮“事件循环”再抛出错误。到了那个时候，Promise 的运行已经结束了，所以这个错误是在 Promise 函数体外抛出的，会冒泡到最外层，成了未捕获的错误。</p>
    <p>一般总是建议，Promise 对象后面要跟<code>catch</code>方法，这样可以处理 Promise 内部发生的错误。<code>catch</code>方法返回的还是一个 Promise
        对象，因此后面还可以接着调用<code>then</code>方法。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">const</span> <span
            class="pl-c1">someAsyncThing</span> <span class="pl-k">=</span> <span class="pl-k">function</span>() {
  <span class="pl-k">return</span> <span class="pl-k">new</span> <span class="pl-en">Promise</span>(<span class="pl-k">function</span>(<span
                class="pl-smi">resolve</span>, <span class="pl-smi">reject</span>) {
    <span class="pl-c"><span class="pl-c">//</span> 下面一行会报错，因为x没有声明</span>
    <span class="pl-en">resolve</span>(x <span class="pl-k">+</span> <span class="pl-c1">2</span>);
  });
};

<span class="pl-en">someAsyncThing</span>()
.<span class="pl-en">catch</span>(<span class="pl-k">function</span>(<span class="pl-smi">error</span>) {
  <span class="pl-en">console</span>.<span class="pl-c1">log</span>(<span class="pl-s"><span class="pl-pds">'</span>oh no<span
                class="pl-pds">'</span></span>, error);
})
.<span class="pl-en">then</span>(<span class="pl-k">function</span>() {
  <span class="pl-en">console</span>.<span class="pl-c1">log</span>(<span class="pl-s"><span class="pl-pds">'</span>carry on<span
                class="pl-pds">'</span></span>);
});
<span class="pl-c"><span class="pl-c">//</span> oh no [ReferenceError: x is not defined]</span>
<span class="pl-c"><span class="pl-c">//</span> carry on</span></pre>
    </div>
    <p>上面代码运行完<code>catch</code>方法指定的回调函数，会接着运行后面那个<code>then</code>方法指定的回调函数。如果没有报错，则会跳过<code>catch</code>方法。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-c1">Promise</span>.<span
            class="pl-en">resolve</span>()
.<span class="pl-en">catch</span>(<span class="pl-k">function</span>(<span class="pl-smi">error</span>) {
  <span class="pl-en">console</span>.<span class="pl-c1">log</span>(<span class="pl-s"><span class="pl-pds">'</span>oh no<span
                class="pl-pds">'</span></span>, error);
})
.<span class="pl-en">then</span>(<span class="pl-k">function</span>() {
  <span class="pl-en">console</span>.<span class="pl-c1">log</span>(<span class="pl-s"><span class="pl-pds">'</span>carry on<span
                class="pl-pds">'</span></span>);
});
<span class="pl-c"><span class="pl-c">//</span> carry on</span></pre>
    </div>
    <p>
        上面的代码因为没有报错，跳过了<code>catch</code>方法，直接执行后面的<code>then</code>方法。此时，要是<code>then</code>方法里面报错，就与前面的<code>catch</code>无关了。
    </p>
    <p><code>catch</code>方法之中，还能再抛出错误。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">const</span> <span
            class="pl-c1">someAsyncThing</span> <span class="pl-k">=</span> <span class="pl-k">function</span>() {
  <span class="pl-k">return</span> <span class="pl-k">new</span> <span class="pl-en">Promise</span>(<span class="pl-k">function</span>(<span
                class="pl-smi">resolve</span>, <span class="pl-smi">reject</span>) {
    <span class="pl-c"><span class="pl-c">//</span> 下面一行会报错，因为x没有声明</span>
    <span class="pl-en">resolve</span>(x <span class="pl-k">+</span> <span class="pl-c1">2</span>);
  });
};

<span class="pl-en">someAsyncThing</span>().<span class="pl-en">then</span>(<span class="pl-k">function</span>() {
  <span class="pl-k">return</span> <span class="pl-en">someOtherAsyncThing</span>();
}).<span class="pl-en">catch</span>(<span class="pl-k">function</span>(<span class="pl-smi">error</span>) {
  <span class="pl-en">console</span>.<span class="pl-c1">log</span>(<span class="pl-s"><span class="pl-pds">'</span>oh no<span
                class="pl-pds">'</span></span>, error);
  <span class="pl-c"><span class="pl-c">//</span> 下面一行会报错，因为 y 没有声明</span>
  y <span class="pl-k">+</span> <span class="pl-c1">2</span>;
}).<span class="pl-en">then</span>(<span class="pl-k">function</span>() {
  <span class="pl-en">console</span>.<span class="pl-c1">log</span>(<span class="pl-s"><span class="pl-pds">'</span>carry on<span
                class="pl-pds">'</span></span>);
});
<span class="pl-c"><span class="pl-c">//</span> oh no [ReferenceError: x is not defined]</span></pre>
    </div>
    <p>上面代码中，<code>catch</code>方法抛出一个错误，因为后面没有别的<code>catch</code>方法了，导致这个错误不会被捕获，也不会传递到外层。如果改写一下，结果就不一样了。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-en">someAsyncThing</span>().<span
            class="pl-en">then</span>(<span class="pl-k">function</span>() {
  <span class="pl-k">return</span> <span class="pl-en">someOtherAsyncThing</span>();
}).<span class="pl-en">catch</span>(<span class="pl-k">function</span>(<span class="pl-smi">error</span>) {
  <span class="pl-en">console</span>.<span class="pl-c1">log</span>(<span class="pl-s"><span class="pl-pds">'</span>oh no<span
                class="pl-pds">'</span></span>, error);
  <span class="pl-c"><span class="pl-c">//</span> 下面一行会报错，因为y没有声明</span>
  y <span class="pl-k">+</span> <span class="pl-c1">2</span>;
}).<span class="pl-en">catch</span>(<span class="pl-k">function</span>(<span class="pl-smi">error</span>) {
  <span class="pl-en">console</span>.<span class="pl-c1">log</span>(<span class="pl-s"><span class="pl-pds">'</span>carry on<span
                class="pl-pds">'</span></span>, error);
});
<span class="pl-c"><span class="pl-c">//</span> oh no [ReferenceError: x is not defined]</span>
<span class="pl-c"><span class="pl-c">//</span> carry on [ReferenceError: y is not defined]</span></pre>
    </div>
    <p>上面代码中，第二个<code>catch</code>方法用来捕获，前一个<code>catch</code>方法抛出的错误。</p>
    <h2><a href="#promiseall"  class="anchor" id="user-content-promiseall">

    </a>Promise.all()
    </h2>
    <p><code>Promise.all</code>方法用于将多个 Promise 实例，包装成一个新的 Promise 实例。</p>
    <div class="highlight highlight-source-js">
        <pre><span class="pl-k">const</span> <span class="pl-c1">p</span> <span class="pl-k">=</span> <span
                class="pl-c1">Promise</span>.<span class="pl-c1">all</span>([p1, p2, p3]);</pre>
    </div>
    <p>上面代码中，<code>Promise.all</code>方法接受一个数组作为参数，<code>p1</code>、<code>p2</code>、<code>p3</code>都是 Promise
        实例，如果不是，就会先调用下面讲到的<code>Promise.resolve</code>方法，将参数转为 Promise 实例，再进一步处理。（<code>Promise.all</code>方法的参数可以不是数组，但必须具有
        Iterator 接口，且返回的每个成员都是 Promise 实例。）</p>
    <p><code>p</code>的状态由<code>p1</code>、<code>p2</code>、<code>p3</code>决定，分成两种情况。</p>
    <p>（1）只有<code>p1</code>、<code>p2</code>、<code>p3</code>的状态都变成<code>fulfilled</code>，<code>p</code>的状态才会变成<code>fulfilled</code>，此时<code>p1</code>、<code>p2</code>、<code>p3</code>的返回值组成一个数组，传递给<code>p</code>的回调函数。
    </p>
    <p>（2）只要<code>p1</code>、<code>p2</code>、<code>p3</code>之中有一个被<code>rejected</code>，<code>p</code>的状态就变成<code>rejected</code>，此时第一个被<code>reject</code>的实例的返回值，会传递给<code>p</code>的回调函数。
    </p>
    <p>下面是一个具体的例子。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-c"><span
            class="pl-c">//</span> 生成一个Promise对象的数组</span>
<span class="pl-k">const</span> <span class="pl-c1">promises</span> <span class="pl-k">=</span> [<span
                class="pl-c1">2</span>, <span class="pl-c1">3</span>, <span class="pl-c1">5</span>, <span class="pl-c1">7</span>, <span
                class="pl-c1">11</span>, <span class="pl-c1">13</span>].<span class="pl-en">map</span>(<span
                class="pl-k">function</span> (<span class="pl-smi">id</span>) {
  <span class="pl-k">return</span> <span class="pl-en">getJSON</span>(<span class="pl-s"><span class="pl-pds">'</span>/post/<span
                class="pl-pds">'</span></span> <span class="pl-k">+</span> id <span class="pl-k">+</span> <span
                class="pl-s"><span class="pl-pds">"</span>.json<span class="pl-pds">"</span></span>);
});

<span class="pl-c1">Promise</span>.<span class="pl-c1">all</span>(promises).<span class="pl-en">then</span>(<span
                class="pl-k">function</span> (<span class="pl-smi">posts</span>) {
  <span class="pl-c"><span class="pl-c">//</span> ...</span>
}).<span class="pl-en">catch</span>(<span class="pl-k">function</span>(<span class="pl-smi">reason</span>){
  <span class="pl-c"><span class="pl-c">//</span> ...</span>
});</pre>
    </div>
    <p>上面代码中，<code>promises</code>是包含 6 个 Promise 实例的数组，只有这 6
        个实例的状态都变成<code>fulfilled</code>，或者其中有一个变为<code>rejected</code>，才会调用<code>Promise.all</code>方法后面的回调函数。</p>
    <p>下面是另一个例子。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">const</span> <span
            class="pl-c1">databasePromise</span> <span class="pl-k">=</span> <span class="pl-en">connectDatabase</span>();

<span class="pl-k">const</span> <span class="pl-c1">booksPromise</span> <span class="pl-k">=</span> databasePromise
  .<span class="pl-en">then</span>(findAllBooks);

<span class="pl-k">const</span> <span class="pl-c1">userPromise</span> <span class="pl-k">=</span> databasePromise
  .<span class="pl-en">then</span>(getCurrentUser);

<span class="pl-c1">Promise</span>.<span class="pl-c1">all</span>([
  booksPromise,
  userPromise
])
.<span class="pl-en">then</span>(([<span class="pl-smi">books</span>, <span class="pl-smi">user</span>]) <span
                class="pl-k">=&gt;</span> <span class="pl-en">pickTopRecommentations</span>(books, user));</pre>
    </div>
    <p>
        上面代码中，<code>booksPromise</code>和<code>userPromise</code>是两个异步操作，只有等到它们的结果都返回了，才会触发<code>pickTopRecommentations</code>这个回调函数。
    </p>
    <p>注意，如果作为参数的 Promise
        实例，自己定义了<code>catch</code>方法，那么它一旦被<code>rejected</code>，并不会触发<code>Promise.all()</code>的<code>catch</code>方法。
    </p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">const</span> <span class="pl-c1">p1</span> <span
            class="pl-k">=</span> <span class="pl-k">new</span> <span class="pl-en">Promise</span>((<span
            class="pl-smi">resolve</span>, <span class="pl-smi">reject</span>) <span class="pl-k">=&gt;</span> {
  <span class="pl-en">resolve</span>(<span class="pl-s"><span class="pl-pds">'</span>hello<span class="pl-pds">'</span></span>);
})
.<span class="pl-en">then</span>(<span class="pl-smi">result</span> <span class="pl-k">=&gt;</span> result)
.<span class="pl-en">catch</span>(<span class="pl-smi">e</span> <span class="pl-k">=&gt;</span> e);

<span class="pl-k">const</span> <span class="pl-c1">p2</span> <span class="pl-k">=</span> <span class="pl-k">new</span> <span
                class="pl-en">Promise</span>((<span class="pl-smi">resolve</span>, <span
                class="pl-smi">reject</span>) <span class="pl-k">=&gt;</span> {
  <span class="pl-k">throw</span> <span class="pl-k">new</span> <span class="pl-en">Error</span>(<span
                class="pl-s"><span class="pl-pds">'</span>报错了<span class="pl-pds">'</span></span>);
})
.<span class="pl-en">then</span>(<span class="pl-smi">result</span> <span class="pl-k">=&gt;</span> result)
.<span class="pl-en">catch</span>(<span class="pl-smi">e</span> <span class="pl-k">=&gt;</span> e);

<span class="pl-c1">Promise</span>.<span class="pl-c1">all</span>([p1, p2])
.<span class="pl-en">then</span>(<span class="pl-smi">result</span> <span class="pl-k">=&gt;</span> <span class="pl-en">console</span>.<span
                class="pl-c1">log</span>(result))
.<span class="pl-en">catch</span>(<span class="pl-smi">e</span> <span class="pl-k">=&gt;</span> <span class="pl-en">console</span>.<span
                class="pl-c1">log</span>(e));
<span class="pl-c"><span class="pl-c">//</span> ["hello", Error: 报错了]</span></pre>
    </div>
    <p>上面代码中，<code>p1</code>会<code>resolved</code>，<code>p2</code>首先会<code>rejected</code>，但是<code>p2</code>有自己的<code>catch</code>方法，该方法返回的是一个新的
        Promise 实例，<code>p2</code>指向的实际上是这个实例。该实例执行完<code>catch</code>方法后，也会变成<code>resolved</code>，导致<code>Promise.all()</code>方法参数里面的两个实例都会<code>resolved</code>，因此会调用<code>then</code>方法指定的回调函数，而不会调用<code>catch</code>方法指定的回调函数。
    </p>
    <p>如果<code>p2</code>没有自己的<code>catch</code>方法，就会调用<code>Promise.all()</code>的<code>catch</code>方法。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">const</span> <span class="pl-c1">p1</span> <span
            class="pl-k">=</span> <span class="pl-k">new</span> <span class="pl-en">Promise</span>((<span
            class="pl-smi">resolve</span>, <span class="pl-smi">reject</span>) <span class="pl-k">=&gt;</span> {
  <span class="pl-en">resolve</span>(<span class="pl-s"><span class="pl-pds">'</span>hello<span class="pl-pds">'</span></span>);
})
.<span class="pl-en">then</span>(<span class="pl-smi">result</span> <span class="pl-k">=&gt;</span> result);

<span class="pl-k">const</span> <span class="pl-c1">p2</span> <span class="pl-k">=</span> <span class="pl-k">new</span> <span
                class="pl-en">Promise</span>((<span class="pl-smi">resolve</span>, <span
                class="pl-smi">reject</span>) <span class="pl-k">=&gt;</span> {
  <span class="pl-k">throw</span> <span class="pl-k">new</span> <span class="pl-en">Error</span>(<span
                class="pl-s"><span class="pl-pds">'</span>报错了<span class="pl-pds">'</span></span>);
})
.<span class="pl-en">then</span>(<span class="pl-smi">result</span> <span class="pl-k">=&gt;</span> result);

<span class="pl-c1">Promise</span>.<span class="pl-c1">all</span>([p1, p2])
.<span class="pl-en">then</span>(<span class="pl-smi">result</span> <span class="pl-k">=&gt;</span> <span class="pl-en">console</span>.<span
                class="pl-c1">log</span>(result))
.<span class="pl-en">catch</span>(<span class="pl-smi">e</span> <span class="pl-k">=&gt;</span> <span class="pl-en">console</span>.<span
                class="pl-c1">log</span>(e));
<span class="pl-c"><span class="pl-c">//</span> Error: 报错了</span></pre>
    </div>
    <h2><a href="#promiserace"  class="anchor" id="user-content-promiserace">

    </a>Promise.race()
    </h2>
    <p><code>Promise.race</code>方法同样是将多个 Promise 实例，包装成一个新的 Promise 实例。</p>
    <div class="highlight highlight-source-js">
        <pre><span class="pl-k">const</span> <span class="pl-c1">p</span> <span class="pl-k">=</span> <span
                class="pl-c1">Promise</span>.<span class="pl-en">race</span>([p1, p2, p3]);</pre>
    </div>
    <p>上面代码中，只要<code>p1</code>、<code>p2</code>、<code>p3</code>之中有一个实例率先改变状态，<code>p</code>的状态就跟着改变。那个率先改变的 Promise
        实例的返回值，就传递给<code>p</code>的回调函数。</p>
    <p><code>Promise.race</code>方法的参数与<code>Promise.all</code>方法一样，如果不是 Promise
        实例，就会先调用下面讲到的<code>Promise.resolve</code>方法，将参数转为 Promise 实例，再进一步处理。</p>
    <p>下面是一个例子，如果指定时间内没有获得结果，就将 Promise 的状态变为<code>reject</code>，否则变为<code>resolve</code>。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">const</span> <span class="pl-c1">p</span> <span
            class="pl-k">=</span> <span class="pl-c1">Promise</span>.<span class="pl-en">race</span>([
  <span class="pl-en">fetch</span>(<span class="pl-s"><span class="pl-pds">'</span>/resource-that-may-take-a-while<span
                class="pl-pds">'</span></span>),
  <span class="pl-k">new</span> <span class="pl-en">Promise</span>(<span class="pl-k">function</span> (<span
                class="pl-smi">resolve</span>, <span class="pl-smi">reject</span>) {
    <span class="pl-c1">setTimeout</span>(() <span class="pl-k">=&gt;</span> <span class="pl-en">reject</span>(<span
                class="pl-k">new</span> <span class="pl-en">Error</span>(<span class="pl-s"><span
                class="pl-pds">'</span>request timeout<span class="pl-pds">'</span></span>)), <span
                class="pl-c1">5000</span>)
  })
]);
<span class="pl-smi">p</span>.<span class="pl-en">then</span>(<span class="pl-smi">response</span> <span class="pl-k">=&gt;</span> <span
                class="pl-en">console</span>.<span class="pl-c1">log</span>(response));
<span class="pl-smi">p</span>.<span class="pl-en">catch</span>(<span class="pl-smi">error</span> <span class="pl-k">=&gt;</span> <span
                class="pl-en">console</span>.<span class="pl-c1">log</span>(error));</pre>
    </div>
    <p>上面代码中，如果 5 秒之内<code>fetch</code>方法无法返回结果，变量<code>p</code>的状态就会变为<code>rejected</code>，从而触发<code>catch</code>方法指定的回调函数。
    </p>
    <h2><a href="#promiseresolve"  class="anchor" id="user-content-promiseresolve">

    </a>Promise.resolve()
    </h2>
    <p>有时需要将现有对象转为 Promise 对象，<code>Promise.resolve</code>方法就起到这个作用。</p>
    <div class="highlight highlight-source-js">
        <pre><span class="pl-k">const</span> <span class="pl-c1">jsPromise</span> <span class="pl-k">=</span> <span
                class="pl-c1">Promise</span>.<span class="pl-en">resolve</span>(<span class="pl-smi">$</span>.<span
                class="pl-en">ajax</span>(<span class="pl-s"><span class="pl-pds">'</span>/whatever.json<span
                class="pl-pds">'</span></span>));</pre>
    </div>
    <p>上面代码将 jQuery 生成的<code>deferred</code>对象，转为一个新的 Promise 对象。</p>
    <p><code>Promise.resolve</code>等价于下面的写法。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-c1">Promise</span>.<span
            class="pl-en">resolve</span>(<span class="pl-s"><span class="pl-pds">'</span>foo<span
            class="pl-pds">'</span></span>)
<span class="pl-c"><span class="pl-c">//</span> 等价于</span>
<span class="pl-k">new</span> <span class="pl-en">Promise</span>(<span class="pl-smi">resolve</span> <span class="pl-k">=&gt;</span> <span
                class="pl-en">resolve</span>(<span class="pl-s"><span class="pl-pds">'</span>foo<span
                class="pl-pds">'</span></span>))</pre>
    </div>
    <p><code>Promise.resolve</code>方法的参数分成四种情况。</p>
    <p><strong>（1）参数是一个 Promise 实例</strong></p>
    <p>如果参数是 Promise 实例，那么<code>Promise.resolve</code>将不做任何修改、原封不动地返回这个实例。</p>
    <p><strong>（2）参数是一个<code>thenable</code>对象</strong></p>
    <p><code>thenable</code>对象指的是具有<code>then</code>方法的对象，比如下面这个对象。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">let</span> thenable <span class="pl-k">=</span> {
  <span class="pl-en">then</span><span class="pl-k">:</span> <span class="pl-k">function</span>(<span class="pl-smi">resolve</span>, <span
                class="pl-smi">reject</span>) {
    <span class="pl-en">resolve</span>(<span class="pl-c1">42</span>);
  }
};</pre>
    </div>
    <p><code>Promise.resolve</code>方法会将这个对象转为 Promise 对象，然后就立即执行<code>thenable</code>对象的<code>then</code>方法。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">let</span> thenable <span class="pl-k">=</span> {
  <span class="pl-en">then</span><span class="pl-k">:</span> <span class="pl-k">function</span>(<span class="pl-smi">resolve</span>, <span
                class="pl-smi">reject</span>) {
    <span class="pl-en">resolve</span>(<span class="pl-c1">42</span>);
  }
};

<span class="pl-k">let</span> p1 <span class="pl-k">=</span> <span class="pl-c1">Promise</span>.<span class="pl-en">resolve</span>(thenable);
<span class="pl-smi">p1</span>.<span class="pl-en">then</span>(<span class="pl-k">function</span>(<span class="pl-smi">value</span>) {
  <span class="pl-en">console</span>.<span class="pl-c1">log</span>(value);  <span class="pl-c"><span
                class="pl-c">//</span> 42</span>
});</pre>
    </div>
    <p>
        上面代码中，<code>thenable</code>对象的<code>then</code>方法执行后，对象<code>p1</code>的状态就变为<code>resolved</code>，从而立即执行最后那个<code>then</code>方法指定的回调函数，输出
        42。</p>
    <p><strong>（3）参数不是具有<code>then</code>方法的对象，或根本就不是对象</strong></p>
    <p>如果参数是一个原始值，或者是一个不具有<code>then</code>方法的对象，则<code>Promise.resolve</code>方法返回一个新的 Promise
        对象，状态为<code>resolved</code>。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">const</span> <span class="pl-c1">p</span> <span
            class="pl-k">=</span> <span class="pl-c1">Promise</span>.<span class="pl-en">resolve</span>(<span
            class="pl-s"><span class="pl-pds">'</span>Hello<span class="pl-pds">'</span></span>);

<span class="pl-smi">p</span>.<span class="pl-en">then</span>(<span class="pl-k">function</span> (<span
                class="pl-smi">s</span>){
  <span class="pl-en">console</span>.<span class="pl-c1">log</span>(s)
});
<span class="pl-c"><span class="pl-c">//</span> Hello</span></pre>
    </div>
    <p>上面代码生成一个新的 Promise 对象的实例<code>p</code>。由于字符串<code>Hello</code>不属于异步操作（判断方法是字符串对象不具有 then 方法），返回 Promise
        实例的状态从一生成就是<code>resolved</code>，所以回调函数会立即执行。<code>Promise.resolve</code>方法的参数，会同时传给回调函数。</p>
    <p><strong>（4）不带有任何参数</strong></p>
    <p><code>Promise.resolve</code>方法允许调用时不带参数，直接返回一个<code>resolved</code>状态的 Promise 对象。</p>
    <p>所以，如果希望得到一个 Promise 对象，比较方便的方法就是直接调用<code>Promise.resolve</code>方法。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">const</span> <span class="pl-c1">p</span> <span
            class="pl-k">=</span> <span class="pl-c1">Promise</span>.<span class="pl-en">resolve</span>();

<span class="pl-smi">p</span>.<span class="pl-en">then</span>(<span class="pl-k">function</span> () {
  <span class="pl-c"><span class="pl-c">//</span> ...</span>
});</pre>
    </div>
    <p>上面代码的变量<code>p</code>就是一个 Promise 对象。</p>
    <p>需要注意的是，立即<code>resolve</code>的 Promise 对象，是在本轮“事件循环”（event loop）的结束时，而不是在下一轮“事件循环”的开始时。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-c1">setTimeout</span>(<span
            class="pl-k">function</span> () {
  <span class="pl-en">console</span>.<span class="pl-c1">log</span>(<span class="pl-s"><span class="pl-pds">'</span>three<span
                class="pl-pds">'</span></span>);
}, <span class="pl-c1">0</span>);

<span class="pl-c1">Promise</span>.<span class="pl-en">resolve</span>().<span class="pl-en">then</span>(<span
                class="pl-k">function</span> () {
  <span class="pl-en">console</span>.<span class="pl-c1">log</span>(<span class="pl-s"><span
                class="pl-pds">'</span>two<span class="pl-pds">'</span></span>);
});

<span class="pl-en">console</span>.<span class="pl-c1">log</span>(<span class="pl-s"><span
                class="pl-pds">'</span>one<span class="pl-pds">'</span></span>);

<span class="pl-c"><span class="pl-c">//</span> one</span>
<span class="pl-c"><span class="pl-c">//</span> two</span>
<span class="pl-c"><span class="pl-c">//</span> three</span></pre>
    </div>
    <p>上面代码中，<code>setTimeout(fn, 0)</code>在下一轮“事件循环”开始时执行，<code>Promise.resolve()</code>在本轮“事件循环”结束时执行，<code>console.log('one')</code>则是立即执行，因此最先输出。
    </p>
    <h2><a href="#promisereject"  class="anchor" id="user-content-promisereject">

    </a>Promise.reject()
    </h2>
    <p><code>Promise.reject(reason)</code>方法也会返回一个新的 Promise 实例，该实例的状态为<code>rejected</code>。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">const</span> <span class="pl-c1">p</span> <span
            class="pl-k">=</span> <span class="pl-c1">Promise</span>.<span class="pl-en">reject</span>(<span
            class="pl-s"><span class="pl-pds">'</span>出错了<span class="pl-pds">'</span></span>);
<span class="pl-c"><span class="pl-c">//</span> 等同于</span>
<span class="pl-k">const</span> <span class="pl-c1">p</span> <span class="pl-k">=</span> <span
                class="pl-k">new</span> <span class="pl-en">Promise</span>((<span class="pl-smi">resolve</span>, <span
                class="pl-smi">reject</span>) <span class="pl-k">=&gt;</span> <span class="pl-en">reject</span>(<span
                class="pl-s"><span class="pl-pds">'</span>出错了<span class="pl-pds">'</span></span>))

<span class="pl-smi">p</span>.<span class="pl-en">then</span>(<span class="pl-c1">null</span>, <span class="pl-k">function</span> (<span
                class="pl-smi">s</span>) {
  <span class="pl-en">console</span>.<span class="pl-c1">log</span>(s)
});
<span class="pl-c"><span class="pl-c">//</span> 出错了</span></pre>
    </div>
    <p>上面代码生成一个 Promise 对象的实例<code>p</code>，状态为<code>rejected</code>，回调函数会立即执行。</p>
    <p>注意，<code>Promise.reject()</code>方法的参数，会原封不动地作为<code>reject</code>的理由，变成后续方法的参数。这一点与<code>Promise.resolve</code>方法不一致。
    </p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">const</span> <span class="pl-c1">thenable</span> <span
            class="pl-k">=</span> {
  <span class="pl-en">then</span>(<span class="pl-smi">resolve</span>, <span class="pl-smi">reject</span>) {
    <span class="pl-en">reject</span>(<span class="pl-s"><span class="pl-pds">'</span>出错了<span
                class="pl-pds">'</span></span>);
  }
};

<span class="pl-c1">Promise</span>.<span class="pl-en">reject</span>(thenable)
.<span class="pl-en">catch</span>(<span class="pl-smi">e</span> <span class="pl-k">=&gt;</span> {
  <span class="pl-en">console</span>.<span class="pl-c1">log</span>(e <span class="pl-k">===</span> thenable)
})
<span class="pl-c"><span class="pl-c">//</span> true</span></pre>
    </div>
    <p>
        上面代码中，<code>Promise.reject</code>方法的参数是一个<code>thenable</code>对象，执行以后，后面<code>catch</code>方法的参数不是<code>reject</code>抛出的“出错了”这个字符串，而是<code>thenable</code>对象。
    </p>
    <h2><a href="#两个有用的附加方法"  class="anchor" id="user-content-两个有用的附加方法">

    </a>两个有用的附加方法
    </h2>
    <p>ES6 的 Promise API 提供的方法不是很多，有些有用的方法可以自己部署。下面介绍如何部署两个不在 ES6 之中、但很有用的方法。</p>
    <h3><a href="#done"  class="anchor" id="user-content-done">

    </a>done()
    </h3>
    <p>Promise 对象的回调链，不管以<code>then</code>方法或<code>catch</code>方法结尾，要是最后一个方法抛出错误，都有可能无法捕捉到（因为 Promise
        内部的错误不会冒泡到全局）。因此，我们可以提供一个<code>done</code>方法，总是处于回调链的尾端，保证抛出任何可能出现的错误。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-en">asyncFunc</span>()
  .<span class="pl-en">then</span>(f1)
  .<span class="pl-en">catch</span>(r1)
  .<span class="pl-en">then</span>(f2)
  .<span class="pl-en">done</span>();</pre>
    </div>
    <p>它的实现代码相当简单。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-c1">Promise</span>.<span
            class="pl-c1">prototype</span>.<span class="pl-en">done</span> <span class="pl-k">=</span> <span
            class="pl-k">function</span> (<span class="pl-smi">onFulfilled</span>, <span
            class="pl-smi">onRejected</span>) {
  <span class="pl-c1">this</span>.<span class="pl-en">then</span>(onFulfilled, onRejected)
    .<span class="pl-en">catch</span>(<span class="pl-k">function</span> (<span class="pl-smi">reason</span>) {
      <span class="pl-c"><span class="pl-c">//</span> 抛出一个全局错误</span>
      <span class="pl-c1">setTimeout</span>(() <span class="pl-k">=&gt;</span> { <span class="pl-k">throw</span> reason }, <span
                class="pl-c1">0</span>);
    });
};</pre>
    </div>
    <p>从上面代码可见，<code>done</code>方法的使用，可以像<code>then</code>方法那样用，提供<code>fulfilled</code>和<code>rejected</code>状态的回调函数，也可以不提供任何参数。但不管怎样，<code>done</code>都会捕捉到任何可能出现的错误，并向全局抛出。
    </p>
    <h3><a href="#finally"  class="anchor" id="user-content-finally">

    </a>finally()
    </h3>
    <p><code>finally</code>方法用于指定不管 Promise 对象最后状态如何，都会执行的操作。它与<code>done</code>方法的最大区别，它接受一个普通的回调函数作为参数，该函数不管怎样都必须执行。
    </p>
    <p>下面是一个例子，服务器使用 Promise 处理请求，然后使用<code>finally</code>方法关掉服务器。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-smi">server</span>.<span class="pl-en">listen</span>(<span
            class="pl-c1">0</span>)
  .<span class="pl-en">then</span>(<span class="pl-k">function</span> () {
    <span class="pl-c"><span class="pl-c">//</span> run test</span>
  })
  .<span class="pl-en">finally</span>(<span class="pl-smi">server</span>.<span class="pl-smi">stop</span>);</pre>
    </div>
    <p>它的实现也很简单。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-c1">Promise</span>.<span
            class="pl-c1">prototype</span>.<span class="pl-en">finally</span> <span class="pl-k">=</span> <span
            class="pl-k">function</span> (<span class="pl-smi">callback</span>) {
  <span class="pl-k">let</span> <span class="pl-c1">P</span> <span class="pl-k">=</span> <span class="pl-c1">this</span>.<span
                class="pl-c1">constructor</span>;
  <span class="pl-k">return</span> <span class="pl-c1">this</span>.<span class="pl-en">then</span>(
    <span class="pl-smi">value</span>  <span class="pl-k">=&gt;</span> <span class="pl-c1">P</span>.<span class="pl-en">resolve</span>(<span
                class="pl-en">callback</span>()).<span class="pl-en">then</span>(() <span class="pl-k">=&gt;</span> value),
    <span class="pl-smi">reason</span> <span class="pl-k">=&gt;</span> <span class="pl-c1">P</span>.<span class="pl-en">resolve</span>(<span
                class="pl-en">callback</span>()).<span class="pl-en">then</span>(() <span
                class="pl-k">=&gt;</span> { <span class="pl-k">throw</span> reason })
  );
};</pre>
    </div>
    <p>上面代码中，不管前面的 Promise 是<code>fulfilled</code>还是<code>rejected</code>，都会执行回调函数<code>callback</code>。</p>
    <h2><a href="#应用"  class="anchor" id="user-content-应用">

    </a>应用
    </h2>
    <h3><a href="#加载图片"  class="anchor" id="user-content-加载图片">

    </a>加载图片
    </h3>
    <p>我们可以将图片的加载写成一个<code>Promise</code>，一旦加载完成，<code>Promise</code>的状态就发生变化。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">const</span> <span
            class="pl-c1">preloadImage</span> <span class="pl-k">=</span> <span class="pl-k">function</span> (<span
            class="pl-smi">path</span>) {
  <span class="pl-k">return</span> <span class="pl-k">new</span> <span class="pl-en">Promise</span>(<span class="pl-k">function</span> (<span
                class="pl-smi">resolve</span>, <span class="pl-smi">reject</span>) {
    <span class="pl-k">const</span> <span class="pl-c1">image</span> <span class="pl-k">=</span> <span
                class="pl-k">new</span> <span class="pl-en">Image</span>();
    <span class="pl-smi">image</span>.<span class="pl-smi">onload</span>  <span class="pl-k">=</span> resolve;
    <span class="pl-smi">image</span>.<span class="pl-smi">onerror</span> <span class="pl-k">=</span> reject;
    <span class="pl-smi">image</span>.<span class="pl-smi">src</span> <span class="pl-k">=</span> path;
  });
};</pre>
    </div>
    <h3><a href="#generator-函数与-promise-的结合"  class="anchor"
           id="user-content-generator-函数与-promise-的结合">

    </a>Generator 函数与 Promise 的结合
    </h3>
    <p>使用 Generator 函数管理流程，遇到异步操作的时候，通常返回一个<code>Promise</code>对象。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">function</span> <span class="pl-en">getFoo</span> () {
  <span class="pl-k">return</span> <span class="pl-k">new</span> <span class="pl-en">Promise</span>(<span class="pl-k">function</span> (<span
                class="pl-smi">resolve</span>, <span class="pl-smi">reject</span>){
    <span class="pl-en">resolve</span>(<span class="pl-s"><span class="pl-pds">'</span>foo<span class="pl-pds">'</span></span>);
  });
}

<span class="pl-k">const</span> <span class="pl-c1">g</span> <span class="pl-k">=</span> <span
                class="pl-k">function</span><span class="pl-k">*</span> () {
  <span class="pl-k">try</span> {
    <span class="pl-k">const</span> <span class="pl-c1">foo</span> <span class="pl-k">=</span> <span
                class="pl-k">yield</span> <span class="pl-en">getFoo</span>();
    <span class="pl-en">console</span>.<span class="pl-c1">log</span>(foo);
  } <span class="pl-k">catch</span> (e) {
    <span class="pl-en">console</span>.<span class="pl-c1">log</span>(e);
  }
};

<span class="pl-k">function</span> <span class="pl-en">run</span> (<span class="pl-smi">generator</span>) {
  <span class="pl-k">const</span> <span class="pl-c1">it</span> <span class="pl-k">=</span> <span class="pl-en">generator</span>();

  <span class="pl-k">function</span> <span class="pl-en">go</span>(<span class="pl-smi">result</span>) {
    <span class="pl-k">if</span> (<span class="pl-smi">result</span>.<span class="pl-smi">done</span>) <span
                class="pl-k">return</span> <span class="pl-smi">result</span>.<span class="pl-c1">value</span>;

    <span class="pl-k">return</span> <span class="pl-smi">result</span>.<span class="pl-c1">value</span>.<span
                class="pl-en">then</span>(<span class="pl-k">function</span> (<span class="pl-smi">value</span>) {
      <span class="pl-k">return</span> <span class="pl-en">go</span>(<span class="pl-smi">it</span>.<span class="pl-c1">next</span>(value));
    }, <span class="pl-k">function</span> (<span class="pl-smi">error</span>) {
      <span class="pl-k">return</span> <span class="pl-en">go</span>(<span class="pl-smi">it</span>.<span class="pl-en">throw</span>(error));
    });
  }

  <span class="pl-en">go</span>(<span class="pl-smi">it</span>.<span class="pl-c1">next</span>());
}

<span class="pl-en">run</span>(g);</pre>
    </div>
    <p>上面代码的 Generator 函数<code>g</code>之中，有一个异步操作<code>getFoo</code>，它返回的就是一个<code>Promise</code>对象。函数<code>run</code>用来处理这个<code>Promise</code>对象，并调用下一个<code>next</code>方法。
    </p>
    <h2><a href="#promisetry"  class="anchor" id="user-content-promisetry">

    </a>Promise.try()
    </h2>
    <p>实际开发中，经常遇到一种情况：不知道或者不想区分，函数<code>f</code>是同步函数还是异步操作，但是想用 Promise 来处理它。因为这样就可以不管<code>f</code>是否包含异步操作，都用<code>then</code>方法指定下一步流程，用<code>catch</code>方法处理<code>f</code>抛出的错误。一般就会采用下面的写法。
    </p>
    <div class="highlight highlight-source-js">
        <pre><span class="pl-c1">Promise</span>.<span class="pl-en">resolve</span>().<span class="pl-en">then</span>(f)</pre>
    </div>
    <p>上面的写法有一个缺点，就是如果<code>f</code>是同步函数，那么它会在本轮事件循环的末尾执行。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">const</span> <span class="pl-c1">f</span> <span
            class="pl-k">=</span> () <span class="pl-k">=&gt;</span> <span class="pl-en">console</span>.<span
            class="pl-c1">log</span>(<span class="pl-s"><span class="pl-pds">'</span>now<span
            class="pl-pds">'</span></span>);
<span class="pl-c1">Promise</span>.<span class="pl-en">resolve</span>().<span class="pl-en">then</span>(f);
<span class="pl-en">console</span>.<span class="pl-c1">log</span>(<span class="pl-s"><span
                class="pl-pds">'</span>next<span class="pl-pds">'</span></span>);
<span class="pl-c"><span class="pl-c">//</span> next</span>
<span class="pl-c"><span class="pl-c">//</span> now</span></pre>
    </div>
    <p>上面代码中，函数<code>f</code>是同步的，但是用 Promise 包装了以后，就变成异步执行了。</p>
    <p>那么有没有一种方法，让同步函数同步执行，异步函数异步执行，并且让它们具有统一的 API 呢？回答是可以的，并且还有两种写法。第一种写法是用<code>async</code>函数来写。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">const</span> <span class="pl-c1">f</span> <span
            class="pl-k">=</span> () <span class="pl-k">=&gt;</span> <span class="pl-en">console</span>.<span
            class="pl-c1">log</span>(<span class="pl-s"><span class="pl-pds">'</span>now<span
            class="pl-pds">'</span></span>);
(<span class="pl-k">async</span> () <span class="pl-k">=&gt;</span> <span class="pl-en">f</span>())();
<span class="pl-en">console</span>.<span class="pl-c1">log</span>(<span class="pl-s"><span
                class="pl-pds">'</span>next<span class="pl-pds">'</span></span>);
<span class="pl-c"><span class="pl-c">//</span> now</span>
<span class="pl-c"><span class="pl-c">//</span> next</span></pre>
    </div>
    <p>
        上面代码中，第二行是一个立即执行的匿名函数，会立即执行里面的<code>async</code>函数，因此如果<code>f</code>是同步的，就会得到同步的结果；如果<code>f</code>是异步的，就可以用<code>then</code>指定下一步，就像下面的写法。
    </p>
    <div class="highlight highlight-source-js"><pre>(<span class="pl-k">async</span> () <span class="pl-k">=&gt;</span> <span
            class="pl-en">f</span>())()
.<span class="pl-en">then</span>(<span class="pl-k">...</span>)</pre>
    </div>
    <p>需要注意的是，<code>async () =&gt; f()</code>会吃掉<code>f()</code>抛出的错误。所以，如果想捕获错误，要使用<code>promise.catch</code>方法。</p>
    <div class="highlight highlight-source-js"><pre>(<span class="pl-k">async</span> () <span class="pl-k">=&gt;</span> <span
            class="pl-en">f</span>())()
.<span class="pl-en">then</span>(<span class="pl-k">...</span>)
.<span class="pl-en">catch</span>(<span class="pl-k">...</span>)</pre>
    </div>
    <p>第二种写法是使用<code>new Promise()</code>。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">const</span> <span class="pl-c1">f</span> <span
            class="pl-k">=</span> () <span class="pl-k">=&gt;</span> <span class="pl-en">console</span>.<span
            class="pl-c1">log</span>(<span class="pl-s"><span class="pl-pds">'</span>now<span
            class="pl-pds">'</span></span>);
(
  () <span class="pl-k">=&gt;</span> <span class="pl-k">new</span> <span class="pl-en">Promise</span>(
    <span class="pl-smi">resolve</span> <span class="pl-k">=&gt;</span> <span class="pl-en">resolve</span>(<span
                class="pl-en">f</span>())
  )
)();
<span class="pl-en">console</span>.<span class="pl-c1">log</span>(<span class="pl-s"><span
                class="pl-pds">'</span>next<span class="pl-pds">'</span></span>);
<span class="pl-c"><span class="pl-c">//</span> now</span>
<span class="pl-c"><span class="pl-c">//</span> next</span></pre>
    </div>
    <p>上面代码也是使用立即执行的匿名函数，执行<code>new Promise()</code>。这种情况下，同步函数也是同步执行的。</p>
    <p>鉴于这是一个很常见的需求，所以现在有一个<a href="https://github.com/ljharb/proposal-promise-try">提案</a>，提供<code>Promise.try</code>方法替代上面的写法。
    </p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">const</span> <span class="pl-c1">f</span> <span
            class="pl-k">=</span> () <span class="pl-k">=&gt;</span> <span class="pl-en">console</span>.<span
            class="pl-c1">log</span>(<span class="pl-s"><span class="pl-pds">'</span>now<span
            class="pl-pds">'</span></span>);
<span class="pl-c1">Promise</span>.<span class="pl-en">try</span>(f);
<span class="pl-en">console</span>.<span class="pl-c1">log</span>(<span class="pl-s"><span
                class="pl-pds">'</span>next<span class="pl-pds">'</span></span>);
<span class="pl-c"><span class="pl-c">//</span> now</span>
<span class="pl-c"><span class="pl-c">//</span> next</span></pre>
    </div>
    <p>事实上，<code>Promise.try</code>存在已久，Promise 库<a href="http://bluebirdjs.com/docs/api/promise.try.html"
                                                    rel="nofollow"><code>Bluebird</code></a>、<a
            href="https://github.com/kriskowal/q/wiki/API-Reference#promisefcallargs"><code>Q</code></a>和<a
            href="https://github.com/cujojs/when/blob/master/docs/api.md#whentry"><code>when</code></a>，早就提供了这个方法。</p>
    <p>由于<code>Promise.try</code>为所有操作提供了统一的处理机制，所以如果想用<code>then</code>方法管理流程，最好都用<code>Promise.try</code>包装一下。这样有<a
            href="http://cryto.net/%7Ejoepie91/blog/2016/05/11/what-is-promise-try-and-why-does-it-matter/"
            rel="nofollow">许多好处</a>，其中一点就是可以更好地管理异常。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">function</span> <span
            class="pl-en">getUsername</span>(<span class="pl-smi">userId</span>) {
  <span class="pl-k">return</span> <span class="pl-smi">database</span>.<span class="pl-smi">users</span>.<span
                class="pl-c1">get</span>({id<span class="pl-k">:</span> userId})
  .<span class="pl-en">then</span>(<span class="pl-k">function</span>(<span class="pl-smi">user</span>) {
    <span class="pl-k">return</span> <span class="pl-smi">user</span>.<span class="pl-c1">name</span>;
  });
}</pre>
    </div>
    <p>上面代码中，<code>database.users.get()</code>返回一个 Promise 对象，如果抛出异步错误，可以用<code>catch</code>方法捕获，就像下面这样写。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-smi">database</span>.<span
            class="pl-smi">users</span>.<span class="pl-c1">get</span>({id<span class="pl-k">:</span> userId})
.<span class="pl-en">then</span>(<span class="pl-k">...</span>)
.<span class="pl-en">catch</span>(<span class="pl-k">...</span>)</pre>
    </div>
    <p>但是<code>database.users.get()</code>可能还会抛出同步错误（比如数据库连接错误，具体要看实现方法），这时你就不得不用<code>try...catch</code>去捕获。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">try</span> {
  <span class="pl-smi">database</span>.<span class="pl-smi">users</span>.<span class="pl-c1">get</span>({id<span
                class="pl-k">:</span> userId})
  .<span class="pl-en">then</span>(<span class="pl-k">...</span>)
  .<span class="pl-en">catch</span>(<span class="pl-k">...</span>)
} <span class="pl-k">catch</span> (e) {
  <span class="pl-c"><span class="pl-c">//</span> ...</span>
}</pre>
    </div>
    <p>上面这样的写法就很笨拙了，这时就可以统一用<code>promise.catch()</code>捕获所有同步和异步的错误。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-c1">Promise</span>.<span
            class="pl-en">try</span>(<span class="pl-smi">database</span>.<span class="pl-smi">users</span>.<span
            class="pl-c1">get</span>({id<span class="pl-k">:</span> userId}))
  .<span class="pl-en">then</span>(<span class="pl-k">...</span>)
  .<span class="pl-en">catch</span>(<span class="pl-k">...</span>)</pre>
    </div>
    <p>事实上，<code>Promise.try</code>就是模拟<code>try</code>代码块，就像<code>promise.catch</code>模拟的是<code>catch</code>代码块。</p>
</div>
</body>
</html>
